/**
 * \file  antiviral.h
 * \brief Definition of Antiviral class.
 *
 * ASO: Antiviral Sequence Optimizer
 * <http://aso.googlecode.com/>
 * Copyright (C) 2010 Velez Ezequiel, Peralta Andres, FuDePAN
 *
 * This file is part of the ASO project.
 *
 * Contents:       Header file for ASO providing class Antiviral.
 *
 * System:         ASO
 * Language:       C++
 *
 * Author:         Velez Ezequiel, Peralta Andres
 * E-Mail:
 *
 * ASO is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ASO is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ASO.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef ANTIVIRAL_IMP_H
#define ANTIVIRAL_IMP_H

#include <mili/mili.h>
#include "aso-core/antiviral.h"
#include "aso-core/distance_matrix.h"
#include "aso-core/optimized_container.h"

const biopp::NucDistance MAX_VAL = biopp::NucDistance(3);
const biopp::NucDistance ZERO_DISTANCE = biopp::NucDistance(0);

/*! Clase del antiviral. */
enum AvClass
{
    cPI, /*! Class Protease Inhibitor.         */
    cNRTI, /*! Class Nucletotide Reverse Transcriptasa Inhibitor  */
    cNNRTI, /*! Class Non Nucletotide Reverse Transcriptase Inhibitor */
    cComb    /*! Combinated class.           */
};

/*! Tipo del Antiviral. */
enum AvType
{
    tRT, /*! Type Reverse Transcriptasa. */
    tPI, /*! Type Proteasa Inhibitor  */
    tComb /*! Combinated Type    */
};

/*! Para que funcióne bien el filter con enumerados*/
std::ostream& operator<< (std::ostream& out, const AvType& atype);
std::ostream& operator<< (std::ostream& out, const AvClass& aclass);
std::istream& operator>> (std::istream& is, AvClass& aclass);
std::istream& operator>> (std::istream& is, AvType& atype);

/*! Clase Antiviral:
 Clase que implementa a un antiviral asociandoles una secuencia de aminoacidos, un nombre, un tipo,
 una clase y un id unico.
*/
class ResisBasedAntiviral: public Antiviral
{
public:
    /* Posición de las Resistencias*/
    struct ResistancePosition
    {
        size_t pos;
        string  aminoacids;

        ResistancePosition(size_t pos, const std::string aminoacids)
            : pos(pos),
              aminoacids(aminoacids)
        {}

        ResistancePosition(const ResistancePosition& other)
            : pos(other.pos),
              aminoacids(other.aminoacids)
        {}

        ~ResistancePosition() {}

        //Retorna un par con la lista de tripletes y la distancia mínima.
        TripletVal min_dist_to_resistance(const biopp::PseudoNucSequence& sequence) const
        {
            TripletVal min_list;

            OptimizedContainer<TripletList, biopp::NucDistance, MinimumValues, IncludingZero> keepMinValue(min_list.first, MAX_VAL);

            const biopp::PseudoTriplet pos_triplet = sequence.get_triplet(pos);

            for (size_t i = 0; i < aminoacids.size(); ++i)
            {
                for (biopp::TripletsIterator itResTriplets = biopp::GeneticCode::triplets(aminoacids[i]); !itResTriplets.end(); ++itResTriplets)
                {
                    for (biopp::TripletsIterator itPureT = pos_triplet.getPureTriplets(); !itPureT.end(); ++itPureT)
                    {
                        const biopp::NucDistance distance = (*itResTriplets).distance(*itPureT, matrix);
                        keepMinValue.add(*itResTriplets, distance);
                    }
                }
            }

            min_list.second = keepMinValue.getBestValue();

            return min_list;
        }
    };

    /* Lista de Resistencias */
    typedef std::list<ResistancePosition> Resistances;

private:
    Resistances     resistances;

public:

    /*! Constructor del tipo antiviral con todos los parametros */
    ResisBasedAntiviral(const std::string& name, size_t id, AvType avtype, AvClass avclass)
        : Antiviral(name)
    {
        set_attrib("id", id);
        set_attrib("class", avclass);
        set_attrib("Type", avtype);
    }

    /*! Constructor por copia */
    ResisBasedAntiviral(const ResisBasedAntiviral& other)
        : Antiviral(other), resistances(other.resistances)
    {}

    /*! Obtener la lista de resistencias */
    inline const Resistances& get_resistances() const
    {
        return resistances;
    }

    /*! Agregar una resistencia en una posicion determinada. */
    ResisBasedAntiviral& add_resistance(size_t pos, const std::string& aminoacid);

    /*! Agregar un nuevo atributo al antiviral.*/
    ResisBasedAntiviral& add_attribute(const std::string& attrib, const std::string& value);

    /*! Imprime la información del Antiviral.*/
    void print_antiviral() const;

    /*! Imprime las resistencas. */
    void print_resistances() const;

    /*! Combina otro antiviral con este */
    void combine_with(ResisBasedAntiviral& other) const;

    /*! Dada una secuencia, saber si esta aplica a este antiviral */
    virtual biopp::NucDistance applies(const biopp::PseudoNucSequence& sequence) const;

    /*! Devuelve las mutantes luego de aplicar este antiviral a la secuencia */
    virtual void mutate_sequence(const biopp::PseudoNucSequence& sequence, MutantDist& mutations) const;
};

/*! Una mutante describe la secuencia y el antiviral que se aplico */
typedef std::pair<Antiviral, biopp::PseudoNucSequence> Mutant;

#endif
